﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EasyMvvm
{
    public sealed class ServiceLocator : IServiceProvider
    {
        static ServiceLocator instance = new ServiceLocator();
        public static ServiceLocator Instance
        {
            get
            {
                return instance;
            }
        }

        static ServiceLocator()
        {
            DesignFunctionAttribute.RunDesignTimeFunctions();
        }  

        Dictionary<Type, object> serviceMap = new Dictionary<Type, object>();

        public void Register<T>(T service)
        {
            lock (serviceMap)
            {
                Type type = typeof(T);
                if (serviceMap.ContainsKey(type))
                    serviceMap.Remove(type);

                serviceMap[type] = service;
            }
        }

        public void Register<T>(Func<T> method)
        {
            lock (serviceMap)
            {
                Type type = typeof(T);
                if (serviceMap.ContainsKey(type))
                    serviceMap.Remove(type);

                serviceMap[type] = method;
            }

        }

        public bool Unregister<T>(T service)
        {
            lock (serviceMap)
            {
                Type type = typeof(T);
                if (serviceMap.ContainsKey(type) && object.Equals(serviceMap[type], service))
                    return serviceMap.Remove(type);
                else
                    return false;
            }
        }

        public bool Unregister<T>(Func<T> method)
        {
            lock (serviceMap)
            {
                Type type = typeof(T);
                if (serviceMap.ContainsKey(type) && object.Equals(serviceMap[type], method))
                    return serviceMap.Remove(type);
                else
                    return false;
            }
        }

        public T Get<T>()
        {
            return Get<T>(false);
        }

        public T Get<T>(bool throwIfNull)
        {
            lock (serviceMap)
            {
                Type type = typeof(T);
                if (!serviceMap.ContainsKey(type))
                {
                    if (throwIfNull)
                        throw new ArgumentException("No service was found!");
                    else
                        return default(T);
                }
                object value = serviceMap[type];
                T result;
                if (value.GetType() == typeof(Func<T>))
                {
                    result = ((Func<T>)value).Invoke();
                }
                else
                {
                    result = (T)value;
                }
                if (throwIfNull && result == null)
                    throw new ArgumentException("No service was found!");

                return result;
            }
        }

        #region IServiceProvider Members

        /// <summary>
        /// For better performance call the generic version!
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        object IServiceProvider.GetService(Type serviceType)
        {
            Type type = this.GetType();
            return type.GetMethod("Get", Type.EmptyTypes).MakeGenericMethod(serviceType).Invoke(this, null);
        }

        #endregion
    }
}
